#ifndef __SC_PROGRAM_H__
#define __SC_PROGRAM_H__

////////////////////////////////////////////////////////////////////////////////
// Includes
////////////////////////////////////////////////////////////////////////////////

#include <vector>
#include <string>
#include <iostream>

#include "Couple.hpp"
#include "Triple.hpp"
#include "Range.hpp"

#include "SCC_LibConfig.h"

////////////////////////////////////////////////////////////////////////////////
// Namespaces
////////////////////////////////////////////////////////////////////////////////

namespace SC {

////////////////////////////////////////////////////////////////////////////////
// Implementation
////////////////////////////////////////////////////////////////////////////////

// Wildcard : character than can replace or be replace by any other
#define WILDCARD '?'

/**
 *  This class defines a "Systemic Computation" compiled program.
 */
class LIBRARY_DECL SCProgram {

	public:
		
		/// The compiler needs to be a friend class to fill in the data
		friend class SCCompiler;
		
		/// Constructor
		SCProgram();

		/// Read a program from a byte-code file
		SCProgram(const std::string & name);
		
		/// Returns the program's name
		const std::string & GetName() const { return this->name; }
		
		/// Returns the program's path
		const std::string & GetPath() const { return this->name; }
		
		/// Returns the program's full path
		const std::string & GetFullPath() const { return this->fullpath; }

		/// Returns the word length
		const unsigned int GetWordLength() const { return this->word_length; }
		
		/// Returns the function code kernel offset bounds
		const Types::Range<int> GetFuncOffset() const { return this->func_offset; }
		
		/// Returns the system names and corresponding bytecode declaration
		const std::vector< Types::Couple<std::string,std::string> > & GetSysDecl() const { return this->sys_decl; }
		
		/// Returns the system instances (type index, name, sub-systems indices)
		const std::vector< Types::Triple<unsigned int, std::string, std::vector<unsigned int> > > & GetSysInst() const { return this->sys_inst; }
		
		/// Returns the compression code
		const std::vector< Types::Couple<char,std::string> > & GetCodes() const { return this->codes; }

		/// Returns the function code to name map
		const std::vector< Types::Couple<std::string,std::string> > & GetFunctions() const { return this->functions; }

		/// Returns the function modules' names
		const std::vector<std::string> & GetFuncModules() const { return this->func_modules; }
		
		/// Build super systems list
		const std::vector<unsigned int> GetSuperSystems(const unsigned int) const;

		/// Print to the given output
		void Print(std::ostream & = std::cout) const;

		/// Write program to the given file
		void WriteByteCode(const std::string &) const;

		/// Read program from the given file
		void ReadByteCode(const std::string &);
		
		/// Clear data
		void Clear();

	protected:

		/// Program's name
		std::string name;
		
		// Program's path
		std::string path;
		
		// Program's file pull path
		std::string fullpath;

		/// Word length
		unsigned int word_length;

		/// Function code kernel offset bounds (<0 if none)
		Types::Range<int> func_offset;
		
		/// System names and bytecode declarations
		std::vector< Types::Couple<std::string,std::string> > sys_decl;

		/// System instances (type index, name, sub-systems indices)
		std::vector< Types::Triple<unsigned int, std::string, std::vector<unsigned int> > > sys_inst;

		/// Compression codes (uncompressed, compressed)
		std::vector< Types::Couple<char,std::string> > codes;

		/// Function codes and names
		std::vector< Types::Couple<std::string,std::string> > functions;
		
		/// Functions module file name
		std::vector<std::string> func_modules;

		/// Read a long string from the input		
		const std::string readLongStringFromStream(
			std::istream &,
			char *,
			const unsigned int,
			const unsigned int
		);

};

////////////////////////////////////////////////////////////////////////////////
// Closing Namespaces
////////////////////////////////////////////////////////////////////////////////

}

////////////////////////////////////////////////////////////////////////////////

#endif
